package com.xusheng.cli.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xusheng.cli.common.base.MyServiceImpl;
import com.xusheng.cli.common.exception.ServiceException;
import com.xusheng.cli.entity.backAgentModule.BackAgent;
import com.xusheng.cli.entity.frontUserModule.FrontUser;
import com.xusheng.cli.entity.paymentChannelModule.PaymentChannel;
import com.xusheng.cli.entity.withdrawModule.Withdraw;
import com.xusheng.cli.mapper.BackAgentMapper;
import com.xusheng.cli.mapper.FrontUserMapper;
import com.xusheng.cli.mapper.PaymentChannelMapper;
import com.xusheng.cli.mapper.WithdrawMapper;
import com.xusheng.cli.service.WithdrawService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 提现(Withdraw)表服务实现类
 *
 * @author whr
 * @since 2024-03-19 17:08:43
 */
@Slf4j
@Service("withdrawService")
public class WithdrawServiceImpl extends MyServiceImpl<WithdrawMapper, Withdraw> implements WithdrawService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private PaymentChannelMapper paymentChannelMapper;
    @Resource
    private FrontUserMapper frontUserMapper;
    @Resource
    private BackAgentMapper backAgentMapper;

    /**
     * @param reqParam
     * @return
     */
    @Override
    public String payNotify(TreeMap<String, String> reqParam) {
        RLock lock = redissonClient.getLock("notify:" + reqParam.get("requestId"));
        try {
            if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
                LambdaQueryWrapper<Withdraw> query = Wrappers.lambdaQuery();
                query.eq(Withdraw::getId, reqParam.get("requestId")).eq(Withdraw::getState, 2);
                Withdraw withdraw = getOne(query);
                //校验订单
                if (withdraw == null) {
                    log.error("订单无效: {}", reqParam);
                    return "SUCCESS";
                }
                PaymentChannel channel = paymentChannelMapper.selectById(withdraw.getPaymentChannelId());
                if (channel == null || channel.getType() == null
                        || StrUtil.hasBlank(channel.getSecret())
                        || channel.getMerchantNumber() == null) {
                    throw new ServiceException("支付渠道号错误");
                }
                Dict dict = Dict.create();
                dict.set("amount", reqParam.get("amount")).
                        set("requestId", reqParam.get("requestId")).
                        set("serialNumber", reqParam.get("serialNumber")).
                        set("status", reqParam.get("status")).
                        set("merchantId", reqParam.get("merchantId"));
                //校验签名
                if (ObjectUtil.notEqual(getSignToken(dict, channel.getSecret()), reqParam.get("code"))) {
                    log.error("代付订单:{} , 支付订单:{}, 回调签名认证失败", reqParam.get("requestId"), reqParam.get("serialNumber"));
                    return "SUCCESS";
                }

                // 订单已经处理过 , 直接返回success
                if (withdraw.getState() >= 3) {
                    log.info("代付订单:{} , 支付订单:{}, 代付成功", reqParam.get("requestId"), reqParam.get("serialNumber"));
                    return "SUCCESS";
                }
                //校验金额
                if (!NumberUtil.equals(new BigDecimal(reqParam.get("amount")), NumberUtil.mul(withdraw.getActualAmount(), 100))) {
                    // throw new ServiceException("付款金额与订单金额不符");
                    log.error("代付订单:{} , 支付订单:{}, 付款金额与订单金额不符", reqParam.get("requestId"), reqParam.get("serialNumber"));
                    return "SUCCESS";
                }
                if ("SUCCESS".equals(reqParam.get("status"))) {
                    // 修改充值状态
                    withdraw.setState(3);
                    withdraw.setPaymentTime(LocalDateTime.now());
                } else {
                    withdraw.setState(4);
                    withdraw.setExceptionInfo("代付回调失败");
                    // 更新账户余额
                    if (withdraw.getType() == 1) {
                        // 商户
                        BackAgent backAgent = backAgentMapper.selectById(withdraw.getUserId());
                        backAgent.setBalance(backAgent.getBalance().add(new BigDecimal(reqParam.get("amount"))));
                        backAgentMapper.updateById(backAgent);
                    } else if (withdraw.getType() == 2) {
                        // 玩家计算剩余金币
                        FrontUser frontUser = frontUserMapper.selectById(withdraw.getUserId());
                        frontUser.setGold(frontUser.getGold() + new BigDecimal(reqParam.get("amount")).multiply(new BigDecimal(10)).intValue());
                        frontUserMapper.updateById(frontUser);
                    }
                }
                updateById(withdraw);

            } else {
                log.error("代付订单:{} , 支付订单:{}, 正在处理中", reqParam.get("requestId"), reqParam.get("serialNumber"));
                return "SUCCESS";
            }
        } catch (Exception e) {
            log.error("代付订单:{} , 支付订单:{}, 支付回调失败,错误信息 :{} ", reqParam.get("requestId"), reqParam.get("serialNumber"), e.getMessage());
            return "SUCCESS";
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        log.info("代付订单:{} , 支付订单:{}, 支付回调处理完成", reqParam.get("requestId"), reqParam.get("serialNumber"));
        return "SUCCESS";
    }

    /**
     * 验签
     *
     * @param map
     * @param appKey
     * @return
     */
    public static String getSignToken(Dict map, String appKey) {

        String result = "";
        try {
            Set<Map.Entry<String, Object>> infoIds = map.entrySet();
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
            List<Map.Entry<String, Object>> collect = infoIds.stream().sorted((o1, o2) -> (o1.getKey()).toString().compareTo(o2.getKey())).collect(Collectors.toList());
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> item : collect) {
                String key = item.getKey();
                Object val = item.getValue();
                if (val != null) {
                    sb.append(val).append("&");
                }
            }
            sb.append(appKey);
            String data = sb.toString();
            result = SecureUtil.md5(data);
        } catch (Exception e) {
            return null;
        }
        return result;
    }

    /**
     * @param beginDate
     * @param endDate
     * @param type
     * @param state
     * @return
     */
    @Override
    public Map<String, Object> statisticsAmount(DateTime beginDate, DateTime endDate, Integer type) {
        return baseMapper.statisticsAmount(beginDate, endDate, type);
    }
}

